package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"golang.org/x/net/context"
	"io/ioutil"
	global2 "longmen/server/config/global"
	"longmen/server/pkg/common/helper/const"
	time2 "longmen/server/pkg/common/helper/timehelper"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/mysql/db_main"
	"longmen/server/pkg/db/mysql/models/models_main"
	"longmen/server/pkg/protobuf"
	"net/http"
	"time"
)

/**
 * @Description //ISO 内支付
 **/
/**
* @Author:admin
* @Description: 生成订单号,并返回一个对象
* @Date: 11:24 2022/6/15
 */
func (s *Server) IsoGetOrderNo(ctx context.Context, req *protobuf.OrderNoReq) (*protobuf.PaymentApply, error) {

	orderNo, _ := util.CreatePaymentOrderNo(_const.PAYMENT_PREFIX_IOS)
	if db_main.InsertPaymentApply(req, orderNo, _const.TERMINAL_APPLE, _const.PAY_CHANNEL_APPLE) > 0 {
		return db_main.GetPaymentOrderNo(int(req.UserId), orderNo)
	}
	return nil, errors.New("申请充值记录插入失入")
}

/**
 * @Author admin
 * @Description //充值
 * @version: v1.8.0
 * @Date 18:58 2022/8/6
 * @Param
 * @return
 **/
func (s *Server) IsoProductPay(ctx context.Context, productPayReq *protobuf.ProductPayReq) (*protobuf.ProductPayResp, error) {
	response := AppleCommonVerify(*productPayReq)
	global2.Log.Debug("IsoPay response:%+v", response)
	if response.Error == nil {
		if response.Response.Status == _const.RESULT_PAY_SUC {
			productPayReq.ProductId = response.Params.ProductId
			return commitProduct(_const.PAY_SUCCESS, productPayReq), nil
		}
	}
	return nil, errors.New(response.Error.Error())
}

/**
 * @Author admin
 * @Description //产品列表List
 * @version: v1.8.0
 * @Date 14:16 2022/8/8
 * @Param
 * @return
 **/

func (s *Server) GetGoldPricesList(ctx context.Context, empty *protobuf.Empty) (*protobuf.GoldPricesListResp, error) {
	ret := &protobuf.GoldPricesListResp{}
	ret.List = db_main.GetGoldPricesList("sequence asc")
	return ret, nil
}

/**
 * @Author willi
 * @Description 支付申请列表查询
 * @Date 14:15 2022/9/5
 * @Param
 * @return
 **/
func (s *Server) GetPaymentList(ctx context.Context, payReq *protobuf.PaymentListReq) (*protobuf.PaymentListResp, error) {
	ret := &protobuf.PaymentListResp{}
	var startDate, endDate string
	if len(payReq.DateOfMonth) > 0 {
		startDate, endDate = time2.GetDateOfMonthFirstAndEnd(payReq.DateOfMonth)
	} else {
		startDate = payReq.StartDate
		endDate = payReq.EndDate
	}
	if list, err := db_main.GetPaymentList(startDate, endDate, payReq.UserId, payReq.Page, payReq.Size); err == nil {
		for _, item := range list {
			if val, ok := _const.PAY_CHANNEL_MAP[item.PayChannel]; ok {
				item.PayChannel = val
			}
			if val, ok := _const.PAY_STATUS_MAP[item.PayStatus]; ok {
				item.PayStatus = val
			}
			if val, ok := _const.PAY_TERMINAL_MAP[item.Terminal]; ok {
				item.Terminal = val
			}
		}
		ret.List = list
		total := db_main.GetPaymentListCount(startDate, endDate, payReq.UserId)
		ret.Total = int32(total)
	}
	return ret, nil
}

func commitProduct(PayStatus int, productPayReq *protobuf.ProductPayReq) *protobuf.ProductPayResp {
	//开始支付 开始事务
	payResp := &protobuf.ProductPayResp{Code: _const.RPC_FAILED}
	session := global2.GetX().Begin()
	//
	if db_main.UpdatePaymentApplyStatus(session, productPayReq) == 0 {
		payResp.Code = _const.RPC_FAILED
		session.Rollback()
		return payResp
	}
	if PayStatus == _const.PAY_SUCCESS {
		paymentApply, _ := db_main.GetPaymentOrderNo(int(productPayReq.UserId), productPayReq.OrderNo)
		if _, afterAmount, err := db_main.UpdateAmountByTx(int(paymentApply.UserId), int(paymentApply.GoldIos+paymentApply.GoldAdded), _const.CoinDiamond, session, _const.WALLET_BALANCE_SHOW, produceProfit(paymentApply)); err == nil {
			payResp.Code = _const.RPC_SUCCEED
			payResp.Balance = int64(afterAmount)
		} else {
			session.Rollback()
			payResp.Code = _const.RPC_FAILED
			return payResp
		}
	}
	//提交事务
	if err := session.Commit().Error; err != nil {
		session.Rollback()
		payResp.Code = _const.RPC_FAILED
	}
	return payResp
}

//更新余额与钱包

func produceProfit(paymentApply *protobuf.PaymentApply) []*models_main.UserProfit {
	//写入用户消费记录
	giftPriceName := _const.EMPTY_STR
	giftPriceName = util.GetConsumeName(17)
	userProfit := &models_main.UserProfit{
		Uid:         int(paymentApply.UserId),
		CoinCount:   int(paymentApply.GoldIos + paymentApply.GoldAdded),
		Content:     fmt.Sprintf("会员id(%d)购买：%d克拉 %s", paymentApply.UserId, (paymentApply.GoldIos+paymentApply.GoldAdded)/100, giftPriceName),
		Type:        _const.FINANCE_DEBIT,
		ConsumeType: _const.PAYMENT,
		Resid:       0,
		Count:       1, //数量为1
		CreateTime:  time.Now(),
		CoinType:    _const.FEE_TYPE_DIAMOND,
		FUid:        0, //没有收款人，所以前台不显示收款人是谁
		OrderNo:     paymentApply.OrderNo,
		Liveid:      0,
	}
	var profitLog []*models_main.UserProfit
	profitLog = append(profitLog, userProfit)
	return profitLog
}

//支付

type Pay struct {
	Params   protobuf.ProductPayReq  `json:"params"`   //请求参数
	Response models_main.ResponsePay `json:"response"` // 返回参数
	Error    error                   `json:"error"`    //错误信息
}

func NewPay() *Pay {
	return &Pay{
		Params:   protobuf.ProductPayReq{},
		Response: models_main.ResponsePay{},
		Error:    nil,
	}
}

// 请求苹果内购校验收据接口
func (this *Pay) RequestReceiptUrl() *Pay {
	if this.Error != nil {
		return this
	}
	resData := models_main.ResponsePay{}
	// 请求数据
	var param struct {
		ReceiptData string `json:"receipt-data"` //(必需的）Base64 编码的收据数据
		Password    string `json:"password"`     //您的应用程序的共享密钥，它是一个十六进制字符串。
		//ExcludeOldTransactions bool   `json:"exclude-old-transactions"` // 将此值设置true为以使响应仅包含任何订阅的最新续订交易。此字段仅用于包含自动续订订阅的应用收据。
	}
	param.Password = this.Params.Password
	if this.Params.TransactionReceipt == "" {
		this.Error = errors.New("缺少凭证数据")
		return this
	}
	// 苹果验证
	param.ReceiptData = this.Params.TransactionReceipt
	client := &http.Client{Timeout: _const.REQUEST_URL_TIMEOUT}
	jsonStr, _ := json.Marshal(param)
	resp, err := client.Post(this.Params.Url, "application/json", bytes.NewBuffer(jsonStr))

	if err != nil {
		this.Error = errors.New(fmt.Sprintf("校验超时, 请稍后再试.%v", err))
		return this
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	global2.Log.Debug("ios back:" + string(body))
	/*	str:="{\n\"receipt\":{\"receipt_type\":\"ProductionSandbox\", \"adam_id\":0, \"app_item_id\":0, \"bundle_id\":\"com.dufig.lieying\", \"application_version\":\"343\", \"download_id\":0, \"version_external_identifier\":0, \"receipt_creation_date\":\"2022-08-09 05:18:28 Etc/GMT\", \"receipt_creation_date_ms\":\"1660022308000\", \"receipt_creation_date_pst\":\"2022-08-08 22:18:28 America/Los_Angeles\", \"request_date\":\"2022-08-09 05:24:56 Etc/GMT\", \"request_date_ms\":\"1660022696583\", \"request_date_pst\":\"2022-08-08 22:24:56 America/Los_Angeles\", \"original_purchase_date\":\"2013-08-01 07:00:00 Etc/GMT\", \"original_purchase_date_ms\":\"1375340400000\", \"original_purchase_date_pst\":\"2013-08-01 00:00:00 America/Los_Angeles\", \"original_application_version\":\"1.0\", \n\"in_app\":[\n{\"quantity\":\"1\", \"product_id\":\"product_item_carat6\", \"transaction_id\":\"2000000125558399\", \"original_transaction_id\":\"2000000125558399\", \"purchase_date\":\"2022-08-09 05:18:28 Etc/GMT\", \"purchase_date_ms\":\"1660022308000\", \"purchase_date_pst\":\"2022-08-08 22:18:28 America/Los_Angeles\", \"original_purchase_date\":\"2022-08-09 05:18:28 Etc/GMT\", \"original_purchase_date_ms\":\"1660022308000\", \"original_purchase_date_pst\":\"2022-08-08 22:18:28 America/Los_Angeles\", \"is_trial_period\":\"false\", \"in_app_ownership_type\":\"PURCHASED\"}]}, \"environment\":\"Sandbox\", \"status\":0}"
		fmt.Println(string(body))
		err = json.Unmarshal([]byte(str), &resData)*/
	err = json.Unmarshal(body, &resData)
	this.Response = resData
	if err != nil {
		this.Error = errors.New(fmt.Sprintf("校验超时, 请稍后再试:%v", err))
		return this
	}
	return this
}

// 自定义校验
func (this *Pay) AppleVerify() *Pay {
	if this.Error != nil {
		return this
	}
	result := this.Response
	if result.Status != 0 {
		if statusMsg, ok := _const.ResponseStatusMsg[result.Status]; ok {
			this.Error = errors.New(statusMsg)
			return this
		}
		this.Error = errors.New(fmt.Sprintf("返回的状态异常:%d", result.Status))
		return this
	}
	// result.Receipt 发送以供验证的收据的 JSON 表示形式。
	if len(result.Receipt.InApp) <= 0 {
		this.Error = errors.New("空凭证")
		return this
	}

	if result.Receipt.BundleId != this.Params.AppleClientId {
		this.Error = errors.New("bundleID 无效")
		return this
	}

	// 取出对应凭证
	for i := 0; i < len(result.Receipt.InApp); i++ {
		val := result.Receipt.InApp[i]
		if this.Params.TransactionId == val.TransactionId {
			this.Params.ProductId = val.ProductId
			break
		}
	}

	if len(this.Params.TransactionId) <= 0 {
		this.Error = errors.New("raw内无对应凭证")
		return this
	}
	if len(this.Params.ProductId) <= 0 {
		this.Error = errors.New("raw内无对应产品")
		return this
	}

	return this
}

// 正式环境-参数
func (this *Pay) AppleProductionParams(params protobuf.ProductPayReq) *Pay {
	this.Params = params
	this.Params.Url = _const.PRODUCTION_URL
	return this
}

// 沙盒环境-参数
func (this *Pay) AppleSandboxParams(params protobuf.ProductPayReq) *Pay {
	this.Params = params
	this.Params.Url = _const.SAND_BOX_URL
	return this
}

// 公共校验(优先校验正式环境,再校验沙盒环境)
func AppleCommonVerify(params protobuf.ProductPayReq) (response *Pay) {
	if params.UrlType == _const.PRODUCTION_URL_VAL {
		response = AppleProductionVerify(params)
	} else {
		response = AppleSandboxVerify(params)
	}
	return response
}

// 正式环境检验
func AppleProductionVerify(params protobuf.ProductPayReq) *Pay {
	newPay := NewPay()
	response := newPay.AppleProductionParams(params).RequestReceiptUrl().AppleVerify()
	return response
}

// 测试环境校验
func AppleSandboxVerify(params protobuf.ProductPayReq) *Pay {
	newPay := NewPay()
	response := newPay.AppleSandboxParams(params).RequestReceiptUrl().AppleVerify()
	return response
}

/*func TestAppleCommonVerify(t *testing.T) {
	params := pb.ProductPayReq{
		AppleClientId:      "", // 固定配置 --> app应用的bundle_id
		Password:           "", // 固定配置--> 应用程序的共享密钥
		TransactionId:      "", // 订单号
		TransactionReceipt: "", // app-提交的-交易凭证
	}
	response := AppleCommonVerify(params)
	if response.Error != nil {
		t.Log("err --> ", fmt.Sprintf("err:%v", response.Error))
	}
	t.Log("response ---> ", fmt.Sprintf("response:%+v", *response))
}
*/
